package main

import (
	"log"
	"os"
	"path"
	"sync"
	"time"
)

type Logic struct {
	tables  map[string]map[string]Store
	logfile *os.File
	mu      sync.RWMutex
}

func (logic *Logic) Initial() {
	logic.PrepareLog()
	// open tables in 3 month
	logic.tables = PrepareStore(*basepath, 3)
}

var stop chan string
var done chan string

func (logic *Logic) Finalize() {
	// close all db
	for date, _ := range logic.tables {
		for name, db := range logic.tables[date] {
			db.Close()
			delete(logic.tables[date], name)
		}
		delete(logic.tables, date)
	}
	log.Println("[Info]: Program exit, close all db")

	// exit go routine
	stop = make(chan string)
	done = make(chan string)
	stop <- "stop"
	<-done
	// close logfile
	log.Println("[Info]: Close logfile")
	logic.logfile.Close()
}

func (logic *Logic) PrepareLog() {
	var err error
	logpath := path.Join(*basepath, *logfile)
	logic.logfile, err = os.OpenFile(logpath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(logic.logfile)
}

func (logic *Logic) CloseDBEveryDay() {
	every_day := time.Tick(10*time.Second)//24*time.Hour)
	go func() {
		for {
			select {
			case <-stop:
				log.Println("[Info]: Exit goroutine CloseDBEveryDay")
                done <- "done"
                return
			case <-every_day:
				log.Println("[Info]: Start closing db before 3 months")
				logic.mu.Lock()
				closeOldDB(logic.tables)
				logic.mu.Unlock()
			default:
				break
			}
		}
	}()
}

func closeOldDB(tables map[string]map[string]Store) error {
	// date:dbname:boltdb
	for date, _ := range tables {
		dbdate, err := time.Parse("2006-01-02", date)
		if err != nil {
			return err
		}
		if dbdate.Before(time.Now().AddDate(0, -3, 0)) {
			for name, db := range tables[date] {
				err := db.Close()
				if err != nil {
					log.Println("[Error]: Error occurs while closing db: " + date + "/" + name)
					return nil
				}
				delete(tables[date], name)
				log.Println("Close db: " + date + "/" + name)
			}
			delete(tables, date)
		}
	}
	return nil
}

func (logic *Logic) OpenNGet(args Args) ([]map[string]string, error) {
	logic.mu.Lock()
	defer logic.mu.Unlock()
	var ret []map[string]string
	mindate, maxdate, err := parseDateTime(args.conds)
	if err != nil {
		return nil, err
	}
	datelist := getDates(mindate, maxdate)
	for _, date := range datelist {
		if logic.tables[date][args.table] == nil {
			tablepath := path.Join(*basepath, date)
			db, _ := GetBoltStore(tablepath, args.table)
			if db != nil {
				if logic.tables[date] == nil {
					logic.tables[date] = make(map[string]Store)
				}
				logic.tables[date][args.table] = db
				log.Printf("[INFO] Open db: %s/%s", date, args.table)
			}
		}
		if logic.tables[date][args.table] != nil {
			result, err := logic.tables[date][args.table].Get(args.conds)
			if err != nil {
				return nil, err
			} else {
				ret = mergeResult(ret, result)
			}
		}
	}
	return ret, nil
}
